package com.lagou.edu.utils;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @Description 根据包路径获取包下的所有类
 * @Classname ClassUtil
 * @Date 2021/3/20 17:04
 * @Author song'xiao
 */
public class ClassUtil {

    public static List<Class<?>> getClassList(boolean childPackage, String... packageNames) throws ClassNotFoundException {
        if (packageNames == null || packageNames.length == 0){
            return new ArrayList<>();
        }
        List<Class<?>> classList = new ArrayList<>();
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        for (String packageName : packageNames) {
            String packagePath = packageName.replace('.', '/');
            URL url = loader.getResource(packagePath);
            if (url != null) {
                String type = url.getProtocol();
                if (Objects.equals(type,"file")) {
                    classList = getClassNameByFile(url.getPath(), null, childPackage);
                } else if (Objects.equals(type,"jar")) {
                    classList = getClassNameByJar(url.getPath(), childPackage);
                }
            } else {
                URL[] urls = ((URLClassLoader) loader).getURLs();
                if (urls != null) {
                    for (int i = 0; i < urls.length; i++) {
                        URL urlTemp = urls[i];
                        String urlPath = urlTemp.getPath();
                        // 不必搜索classes文件夹
                        if (urlPath.endsWith("classes/")) {
                            continue;
                        }
                        String jarPath = urlPath + "!/" + packagePath;
                        classList.addAll(getClassNameByJar(jarPath, childPackage));
                    }
                }

            }
        }

        return classList;
    }

    /**
     *
     * @param filePath  文件路径
     * @param classList 类集合
     * @param childPackage  是否遍历子包
     * @return
     * @throws ClassNotFoundException
     */
    private static List<Class<?>> getClassNameByFile(String filePath, List<Class<?>> classList, boolean childPackage) throws ClassNotFoundException {
        List<Class<?>> myClassList = new ArrayList<>();
        File file = new File(filePath);
        File[] childFiles = file.listFiles();
        for (File childFile : childFiles) {
            if (childFile.isDirectory()) {
                if (childPackage) {
                    myClassList.addAll(getClassNameByFile(childFile.getPath(), myClassList, childPackage));
                }
            } else {
                String childFilePath = childFile.getPath();
                if (childFilePath.endsWith(".class")) {
                    childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9,
                            childFilePath.lastIndexOf("."));
                    childFilePath = childFilePath.replace("\\", ".");
                    myClassList.add(Class.forName(childFilePath));
                }
            }
        }

        return myClassList;
    }

    /**
     *
     * @param jarPath 文件路径
     * @param childPackage 是否遍历子包
     * @return
     */
    private static List<Class<?>> getClassNameByJar(String jarPath, boolean childPackage) {
        List<Class<?>> classList = new ArrayList<>();
        String[] jarInfo = jarPath.split("!");
        String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
        String packagePath = jarInfo[1].substring(1);
        try {
            JarFile jarFile = new JarFile(jarFilePath);
            Enumeration<JarEntry> entrys = jarFile.entries();
            while (entrys.hasMoreElements()) {
                JarEntry jarEntry = entrys.nextElement();
                String entryName = jarEntry.getName();
                if (entryName.endsWith(".class")) {
                    if (childPackage) {
                        if (entryName.startsWith(packagePath)) {
                            entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
                            classList.add(Class.forName(entryName));
                        }
                    } else {
                        int index = entryName.lastIndexOf("/");
                        String myPackagePath;
                        if (index != -1) {
                            myPackagePath = entryName.substring(0, index);
                        } else {
                            myPackagePath = entryName;
                        }
                        if (myPackagePath.equals(packagePath)) {
                            entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
                            classList.add(Class.forName(entryName));
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classList;
    }


}
